<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="NFdEP" id="NFdEP"><span data-lake-id="ub3c77d8d" id="ub3c77d8d">典型回答</span></h1>
  <p data-lake-id="u3fa63dae" id="u3fa63dae"><br></p>
  <p data-lake-id="u336203ba" id="u336203ba"><span data-lake-id="u7838ba7a" id="u7838ba7a">所谓作用域，其实就是说这个东西哪个范围内可以被使用。如我们定义类的成员变量的时候使用的public、private等这些也是作用域的概念。</span></p>
  <p data-lake-id="uc04238cf" id="uc04238cf"><span data-lake-id="u66b0d7f1" id="u66b0d7f1">​</span><br></p>
  <p data-lake-id="u9b1bcba5" id="u9b1bcba5"><span data-lake-id="uf1b8af2f" id="uf1b8af2f">Spring的Bean的作用域，描述的就是这个Bean在哪个范围内可以被使用。不同的作用域决定了了 Bean 的创建、管理和销毁的方式。</span></p>
  <p data-lake-id="u8b574347" id="u8b574347"><span data-lake-id="ucef5a57a" id="ucef5a57a">​</span><br></p>
  <p data-lake-id="ufa397265" id="ufa397265"><span data-lake-id="u6b308ed0" id="u6b308ed0">常见的作用域有</span><strong><span data-lake-id="u0f016b1a" id="u0f016b1a">Singleton、Prototype、Request、Session、Application</span></strong><span data-lake-id="u229d4504" id="u229d4504">这五种。我们在代码中，可以在定义一个Bean的时候，通过@Scope 注解来指定他的作用域：</span></p>
  <p data-lake-id="u6650a22c" id="u6650a22c"><span data-lake-id="u972307a0" id="u972307a0">​</span><br></p>
  <pre lang="java"><code>
@Service
@Scope("prototype")
public class HollisTestService{
    
}
</code></pre>
  <p data-lake-id="u9553fec2" id="u9553fec2"><span data-lake-id="u31ebe2f1" id="u31ebe2f1">​</span><br></p>
  <p data-lake-id="u1f27f30a" id="u1f27f30a"><span data-lake-id="u8df0da4c" id="u8df0da4c">这五种作用域的解释如下：</span></p>
  <p data-lake-id="uf3d7bb85" id="uf3d7bb85"><span data-lake-id="ud2d6232c" id="ud2d6232c">​</span><br></p>
  <ol list="u09916d12">
   <li fid="u44d86af6" data-lake-id="u3bc82f17" id="u3bc82f17"><strong><span data-lake-id="u2481cf3d" id="u2481cf3d">单例（Singleton）</span></strong><span data-lake-id="uec245459" id="uec245459">：</span></li>
  </ol>
  <ul list="uaad0f73f" data-lake-indent="1">
   <li fid="u83d4552d" data-lake-id="ue358fa2d" id="ue358fa2d"><span data-lake-id="uc61c61bc" id="uc61c61bc">默认作用域。</span></li>
   <li fid="u83d4552d" data-lake-id="uc72978a8" id="uc72978a8"><span data-lake-id="ufb61f33f" id="ufb61f33f">对于每个 Spring IoC 容器，只创建一个 Bean 实例。</span></li>
   <li fid="u83d4552d" data-lake-id="udb136223" id="udb136223"><span data-lake-id="uf013839e" id="uf013839e">适用于全局共享的状态。</span></li>
  </ul>
  <ol list="u09916d12" start="2">
   <li fid="u44d86af6" data-lake-id="u6270f78a" id="u6270f78a"><strong><span data-lake-id="u44973446" id="u44973446">原型（Prototype）</span></strong><span data-lake-id="ubce9aad1" id="ubce9aad1">：</span></li>
  </ol>
  <ul list="ud2689070" data-lake-indent="1">
   <li fid="ud2c4349f" data-lake-id="u8954f972" id="u8954f972"><span data-lake-id="ue7acebb5" id="ue7acebb5">每次请求都会创建一个新的 Bean 实例。</span></li>
   <li fid="ud2c4349f" data-lake-id="u364d7c7b" id="u364d7c7b"><span data-lake-id="u8f2638da" id="u8f2638da">适用于所有状态都是非共享的情况。</span></li>
  </ul>
  <ol list="u09916d12" start="3">
   <li fid="u44d86af6" data-lake-id="u7fbd8a9c" id="u7fbd8a9c"><strong><span data-lake-id="u6ba21ff9" id="u6ba21ff9">请求（Request）</span></strong><span data-lake-id="u4c802f73" id="u4c802f73">：</span></li>
  </ol>
  <ul list="u61ade4dd" data-lake-indent="1">
   <li fid="u29305bb4" data-lake-id="u066fa9a6" id="u066fa9a6"><span data-lake-id="u67be1e9e" id="u67be1e9e">仅在 Web 应用程序中有效。</span></li>
   <li fid="u29305bb4" data-lake-id="u32023b35" id="u32023b35"><span data-lake-id="u4da8301e" id="u4da8301e">每个 HTTP 请求都会创建一个新的 Bean 实例。</span></li>
   <li fid="u29305bb4" data-lake-id="u908bc2b4" id="u908bc2b4"><span data-lake-id="u23dd73bf" id="u23dd73bf">用于请求级别的数据存储和处理。</span></li>
  </ul>
  <ol list="u09916d12" start="4">
   <li fid="u44d86af6" data-lake-id="ucd7ca322" id="ucd7ca322"><strong><span data-lake-id="u77d53818" id="u77d53818">会话（Session）</span></strong><span data-lake-id="u0457d469" id="u0457d469">：</span></li>
  </ol>
  <ul list="u4a0b4d82" data-lake-indent="1">
   <li fid="u797e04f1" data-lake-id="ua061eb13" id="ua061eb13"><span data-lake-id="u112289eb" id="u112289eb">仅在 Web 应用程序中有效。</span></li>
   <li fid="u797e04f1" data-lake-id="uc9e6a93f" id="uc9e6a93f"><span data-lake-id="uf3b89983" id="uf3b89983">每个 HTTP 会话都会创建一个新的 Bean 实例。</span></li>
   <li fid="u797e04f1" data-lake-id="uce5870d2" id="uce5870d2"><span data-lake-id="u1083b58b" id="u1083b58b">适用于会话级别的数据存储和处理。</span></li>
  </ul>
  <ol list="u09916d12" start="5">
   <li fid="u44d86af6" data-lake-id="uc83a6fea" id="uc83a6fea"><strong><span data-lake-id="u8ce347b5" id="u8ce347b5">应用（Application）</span></strong><span data-lake-id="u1d93be7b" id="u1d93be7b">：</span></li>
  </ol>
  <ul list="u26dab64a" data-lake-indent="1">
   <li fid="u1bad6d1e" data-lake-id="u761e9b32" id="u761e9b32"><span data-lake-id="u98a7324c" id="u98a7324c">仅在 Web 应用程序中有效。</span></li>
   <li fid="u1bad6d1e" data-lake-id="udb782da8" id="udb782da8"><span data-lake-id="ue12e649a" id="ue12e649a">在 ServletContext 的生命周期内，只创建一个 Bean 实例。</span></li>
   <li fid="u1bad6d1e" data-lake-id="u35f8d8f3" id="u35f8d8f3"><span data-lake-id="u107b45dc" id="u107b45dc">适用于全应用程序级别的共享数据。</span></li>
  </ul>
  <ol list="u09916d12" start="6">
   <li fid="u44d86af6" data-lake-id="uea81d46f" id="uea81d46f"><strong><span data-lake-id="u7a0ab235" id="u7a0ab235">Websocket</span></strong><span data-lake-id="u1295c1ce" id="u1295c1ce">：</span></li>
  </ol>
  <ul list="u599d34d1" data-lake-indent="1">
   <li fid="u1bad6d1e" data-lake-id="u23eb6be7" id="u23eb6be7"><span data-lake-id="ua4f290b1" id="ua4f290b1">仅在 Web 应用程序中有效。</span></li>
   <li fid="u1bad6d1e" data-lake-id="u0b4c541b" id="u0b4c541b"><span data-lake-id="u457926bd" id="u457926bd">在 Websocket 的生命周期内，只创建一个 Bean 实例。</span></li>
   <li fid="u1bad6d1e" data-lake-id="u591b1c6b" id="u591b1c6b"><span data-lake-id="u58359fc7" id="u58359fc7">适用于websocket级别的共享数据。</span></li>
  </ul>
  <p data-lake-id="u9302fa3f" id="u9302fa3f"><br></p>
  <p data-lake-id="u9171fe7c" id="u9171fe7c"><br></p>
  <p data-lake-id="ud17226ea" id="ud17226ea"><span data-lake-id="u7a3f7b9d" id="u7a3f7b9d">一般来说我们都是使用Singleton的作用域，有的时候也会用Prototype，其他几个用得不多。</span></p>
  <p data-lake-id="u3c39cca5" id="u3c39cca5"><span data-lake-id="u2d2c803c" id="u2d2c803c">​</span><br></p>
  <p data-lake-id="u94b0dd49" id="u94b0dd49"><span data-lake-id="ufae9fb73" id="ufae9fb73">以下两张图是Spring官方给的关于singleton和prototype的区别。其实就是会创建一个Bean还是多个Bean的区别：</span></p>
  <p data-lake-id="u216c3b64" id="u216c3b64"><br></p>
  <p data-lake-id="u513da77e" id="u513da77e"><img src="https://cdn.nlark.com/yuque/0/2024/png/5378072/1704286011989-3259a43c-e6c8-4e44-a09b-377264d0309b.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_23%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="u92902316" id="u92902316"><br></p>
  <p data-lake-id="ub7999b92" id="ub7999b92"><img src="https://cdn.nlark.com/yuque/0/2024/png/5378072/1704286017123-71d1b3d0-1865-461c-b956-3c803f42ee6c.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_23%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="uc4fcc1de" id="uc4fcc1de"><br></p>
  <h1 data-lake-id="zBjiH" id="zBjiH"><span data-lake-id="u3b25105f" id="u3b25105f">扩展知识</span></h1>
  <p data-lake-id="u0b87998b" id="u0b87998b"><br></p>
  <h2 data-lake-id="tPb23" id="tPb23"><span data-lake-id="ud7286634" id="ud7286634">作用域与循环依赖</span></h2>
  <p data-lake-id="ubb770e74" id="ubb770e74"><br></p>
  <p data-lake-id="udc17339d" id="udc17339d"><span data-lake-id="u27ce471c" id="u27ce471c">Spring在解决循环依赖时，只解决了单例作用域的，别的作用域没有解决：</span></p>
  <p data-lake-id="u23773b6f" id="u23773b6f"><br></p>
  <p data-lake-id="ud427131b" id="ud427131b"><br></p>
  <h2 data-lake-id="KlS6l" id="KlS6l"><span data-lake-id="ubd2a2b83" id="ubd2a2b83">自定义作用域</span></h2>
  <p data-lake-id="uca844f93" id="uca844f93"><br></p>
  <p data-lake-id="u65ef78d4" id="u65ef78d4"><span data-lake-id="u5bfe0280" id="u5bfe0280">除了Spring官方提供的这些作用域以外，我们还可以自定义我们自己的作用域，Spring提供了这方面的支持。</span></p>
  <p data-lake-id="ud1c0ea67" id="ud1c0ea67"><span data-lake-id="ucbf483c4" id="ucbf483c4">​</span><br></p>
  <p data-lake-id="u05c5c44f" id="u05c5c44f"><span data-lake-id="uc017692c" id="uc017692c">要自定义一个 Spring 的作用域，需要实现 </span><code data-lake-id="u453b70b9" id="u453b70b9"><span data-lake-id="u82beac3f" id="u82beac3f">org.springframework.beans.factory.config.Scope</span></code><span data-lake-id="u8012fcbc" id="u8012fcbc"> 接口。这个接口要求实现几个关键方法来管理 Bean 的生命周期。</span></p>
  <p data-lake-id="u132b9852" id="u132b9852"><span data-lake-id="u6cf9ac07" id="u6cf9ac07">​</span><br></p>
  <pre lang="java"><code>
public interface Scope {

	Object get(String name, ObjectFactory&lt;?&gt; objectFactory);

	@Nullable
	Object remove(String name);

	void registerDestructionCallback(String name, Runnable callback);

	@Nullable
	Object resolveContextualObject(String key);


	@Nullable
	String getConversationId();
}
</code></pre>
  <p data-lake-id="ue5ffc886" id="ue5ffc886"><span data-lake-id="u44698350" id="u44698350">​</span><br></p>
  <p data-lake-id="u3680eff7" id="u3680eff7"><span data-lake-id="u1d161f66" id="u1d161f66">接下来，我们需要实现接口的方法，例如 get（创建或检索 Bean 实例）、remove（销毁 Bean 实例）等。</span></p>
  <p data-lake-id="u805f1562" id="u805f1562"><span data-lake-id="u8ca93752" id="u8ca93752">​</span><br></p>
  <pre lang="java"><code>
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;

public class MyCustomScope implements Scope {

    @Override
    public Object get(String name, ObjectFactory&lt;?&gt; objectFactory) {
        // 实现获取 Bean 的逻辑
        return objectFactory.getObject();
    }

    @Override
    public Object remove(String name) {
        // 实现移除 Bean 的逻辑
        return null;
    }

    @Override
    public void registerDestructionCallback(String name, Runnable callback) {
        // 注册 Bean 销毁时的回调
    }

    @Override
    public Object resolveContextualObject(String key) {
        // 用于解析相关上下文数据
        return null;
    }

    @Override
    public String getConversationId() {
        // 返回当前会话的 ID
        return null;
    }
}

</code></pre>
  <p data-lake-id="u754e19da" id="u754e19da"><span data-lake-id="u06af8f92" id="u06af8f92">​</span><br></p>
  <p data-lake-id="ue632d2a6" id="ue632d2a6"><span data-lake-id="u420fe8bb" id="u420fe8bb">接下来，我们需要 Spring 配置中注册这个自定义的作用域。这可以通过 </span><code data-lake-id="uc35393d8" id="uc35393d8"><span data-lake-id="u3c1df614" id="u3c1df614">ConfigurableBeanFactory.registerScope</span></code><span data-lake-id="u5b092dde" id="u5b092dde"> 方法实现。</span></p>
  <p data-lake-id="u1c6e8b72" id="u1c6e8b72"><span data-lake-id="u829fb537" id="u829fb537">​</span><br></p>
  <pre lang="java"><code>
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {

    @Bean
    public MyCustomScope myCustomScope(ConfigurableBeanFactory beanFactory) {
        MyCustomScope scope = new MyCustomScope();
        beanFactory.registerScope("myCustomScope", scope);
        return scope;
    }
}

</code></pre>
  <p data-lake-id="u2fe9f8de" id="u2fe9f8de"><span data-lake-id="u89cdc06e" id="u89cdc06e">​</span><br></p>
  <p data-lake-id="u918bffe9" id="u918bffe9"><span data-lake-id="uec174d2f" id="uec174d2f">在 Bean 定义中使用自定义的作用域的名称。Spring 容器将会根据你的自定义逻辑来创建和管理这些 Bean。</span></p>
  <p data-lake-id="u4a10ece6" id="u4a10ece6"><span data-lake-id="u7816c28b" id="u7816c28b">​</span><br></p>
  <pre lang="java"><code>
@Component
@Scope("myCustomScope")
public class MyScopedBean {
    // ...
}
</code></pre>
 </body>
</html>